#!/bin/sh
function func()
{
	./get_subfunc_level_name.py $5 $1 $2 $3 $4
}
function level()
{
	./get_func_level.py func_tree.txt
}
function num()
{
	./get_data.py  data.txt $1
}
function latency()
{
	./get_latency.py  data.txt $1
}
function get_func()
{
	./get_pointer_func.py  data.txt
}
function find_function_path()
{
	 find $1 -name "*.[ch]" -exec grep -E  -w $2 {} +| more | grep \(  > function_path.txt
	./get_function_subpath.py function_path.txt $2
}
function match_func_tree()
{
	./get_func_name.py $1 $2
}
function cflow_func()
{
	tree=$(match_func_tree func_tree.txt $2)
	if [ ! -z "$tree" ] ; then
	  return
	fi
	#modify kernel source some symbos,such as __ change into _aa, -> change into bb,
	#only for cflow can see these functions, this work is too long, so only first time need it.
	#after first time, can skip this step
	if [ $3 -eq 1 ]; then
	      ./change.py $1/
	fi

	test_func=$2
	path=$(find_function_path $1 $test_func)
	depth=3

	if [ $4 == 1 ]; then
	  depth=8
	fi

	if [ ! -z "$path" ] || [ ! -n "$path" ] ; then
	  cflow -T -d $depth -i _ -m $test_func $path/*.c $path/*.h $1/include/*.h $1/include/*/*.h -o func_tree_xx.txt 2> clow_err.txt
	elif [ $# -eq 2 ]; then
	  cflow -T -d $depth -i _ -m $test_func $1/fs/*/*.c $1/mm/*.c $1/*/*.h $1/*/*/*.h -o func_tree_xx.txt 2> clow_err.txt
	else :
	  cflow -T -d $depth -i _ -m   vfs_read  $1/fs/*.c  $1/include/linux/*.h $1/fs/ext4/*.c -o func_tree_xx.txt 2> clow_err.txt
	fi
	./change_resume.py func_tree_xx.txt func_tree.txt
}

check_cflow() {
    cflow_version=$(cflow --version 2>&1)
    exit_code=$?

    if [ $exit_code -ne 0 ]; then
        echo "Error: cflow --version returned a non-zero exit code: $exit_code" >&2
        exit 1
    fi

    echo "cflow version: $cflow_version"
    return 0
}

function strstr()
{
	echo $1 | grep $2
}

function do_work()
{
	print_first=0
	i=0
	k=0
	m=0
	Null_i=0
	func_level=-1
	func_index=0
	cur_num=()
	cur_latency={}
	total_latency={}
	total_start_num={}
	cur_level_stat=()
	cur_index={}
	max_array_index=1000000000
	cur_real_index={}
	cur_func={}
	tmp_func={}
	last_level=1000000000
	cur_level=1000000000
	start_num=0
	parent_func={}
	parent_func[0]="tmp"
	parent_func[1]="tmp"
	level_first=0
	start_level=1
	null_func={}
	null_index={}
	null_start={}
	last_null_index=0
	total_last_level=0
	null=0
	pointer_last_cflow=0
	tree_file=func_tree.txt
	last_sub={}
	sub_func_level=1
	old_real_index=-1
	find_again=-1
	while true;
	do
		#show the whole path
		#./checkconfig_origngrap.py func_tree.txt
		#./print_func_path.py $tree_file $func_level $func_index

		#get functionn name
		res=$(func $func_level $func_index $start_num 1 $tree_file)
		real_index=$(func $func_level $func_index $start_num 2 $tree_file)
		if [ -z "$real_index" ]; then
		  real_index=$func_index
		fi
		#echo $res : "level:" $func_level "index:" $func_index "real:" $real_index "start:" $start_num

		if [[ ! -z "$res" && $old_real_index == $real_index ]]; then
			func_index=$(($func_index+1))
			find_again=1
			continue
		fi
		if [[ $find_again == 1 && -z "$res" ]]; then
			func_index=$(($func_index-1))
			func_level=$(($func_level+1))
			find_again=-2
			continue
		fi
		if [[ $find_again == -2 && -z "$res" ]]; then
			func_level=$(($func_level-1))
		fi
		find_again=-1
		if [ ! -z "$res" ]; then
			sub=$(strstr $res "schedule")
			if [[ $sub = "schedule" ]]; then
				func_index=$(($func_index+1))
				continue
			fi
			sub=$(strstr $res "cond_resched")
			if [[ $sub = "cond_resched" ]]; then
				func_index=$(($func_index+1))
				continue
			fi
		fi
		old_real_index=$real_index
		if [[ -z "$res" && $5 == 1 && ! -z "$tmp_func" ]]; then
			#hook function, catch data, print to data.txt
			echo current:${tmp_func[@]}
			./os_stat_blongm ${tmp_func[@]} -n $m -i $print_first -t $2 -b 0 -h $4 --ptr 0 -p $6 -x &> data.txt
			unset tmp_func
			#cat data.txt

			#sub=$(strstr $res "write_iter")
			./checkconfig_origngrap.py data.txt
			if [ $last_null_index -eq 0 ]; then
			    Null_i=0
			fi
			while true;
			do
			  tmp_index=${cur_index[$i - $m]}
			  #echo $i, $m $tmp_index ${cur_func[$i - $m + $k]}, $k

			  #get the data
			  tmp_num=$(num $k)
			  #echo 000000000000000 $tmp_num, $k
			  if [ ! -n "$tmp_num" ] || [ $tmp_num -eq 0 ] && [ $last_null_index -eq 0 ]; then
				  null_func[$Null_i]=${cur_func[$i - $m + $k]}
				  null_index[$Null_i]=$((${cur_index[$i - $m + $k]} - 1))
				  null_start[$Null_i]=${total_start_num[$i - $m + $k]}
				  #echo "save null:" $Null_i, ${null_index[$Null_i]}, ${null_start[$Null_i]}, ${total_start_num[$i - $m + $k]}, ${null_func[$Null_i]}
				  Null_i=$(($Null_i+1))
				  k=$(($k+1))
				  if [ $k -ge $m ];then
				    break
				  fi
				  continue
			  fi
			  #record data
			  cur_num[$i - $m + $k]=$tmp_num
			  tmp_latency=$(latency $k)
			  cur_latency[$i - $m + $k]=$tmp_latency
			  total_latency[$i - $m + $k]=`expr $tmp_latency \* $tmp_num`
			  #echo 000000000000001 ${cur_pointer_func[$i - $m + $k]}, $tmp_num, $tmp_latency, $i - $m + $k, $k
			  k=$(($k+1))
			  if [ $k -ge $m ];then
			    break
			  fi
			done
			m=0
			k=0

			for(( j=0; j<${#cur_num[@]}; j++ )) do
				if [ ${cur_num[j]} != 0 ]; then
					tmp_index=${cur_index[j]}
					if [ ! -z "$start_num" ]; then
					    tmp_index=($tmp_index - $start_num)
					fi
					printf "******real index:%10d, index: %10d, level: %2d, num::%8d, latency:%10d ns, total latency:%10d ns, func:%s, origin:%s\n" ${cur_real_index[j]} $tmp_index ${cur_level_stat[j]} ${cur_num[j]} ${cur_latency[j]} ${total_latency[j]} ${cur_func[j]} ${cur_pointer_func[j]}
				fi
			done

		fi

		if [ -z "$res" ]; then
			if [[ $last_level -eq $cur_level && $last_null_index -eq 0 ]] ; then
				if [ $max_array_index -lt 1000000000 ]; then 
					pointer=$(strstr ${cur_func[$max_array_index]}  "\->")
					if [ -z "$pointer" ]; then
						pointer=$(strstr ${cur_func[$max_array_index]}  "INDIRECT_CALL_INET")
					fi
					#echo $max_array_index ":"  $pointer
					depth=8
					tree_file=sub_func_tree.txt
					sub_func=${cur_pointer_func[$max_array_index]}
					if [ $3 -eq 0 ]; then
						depth=3
						tree_file=sub_func_tree_$sub_func_level.txt
						sub_func_level=$(($sub_func_level+1))
						if [ -z "$pointer" ] && [ "$last_sub" == "$sub_func" ]; then
							break
						fi
						#echo "123 stat finish", $last_sub, $sub_func
					fi
					touch $tree_file
					if [ ! -z "$pointer" ] || [ $3 -eq 0 ]; then
						pointer_last_cflow=${#cur_num[@]};
						tree=$(match_func_tree $tree_file $sub_func)
						last_sub=$sub_func
						if [ -z "$tree" ] ; then
						  old_real_index=-1
						  path=$(find_function_path $1 $sub_func)
						  echo 1-----cflow next level:$1, $sub_func, $path---------
						  if [ ! -z "$1" ] && [ ! -z "$path" ]; then
							  cflow -T -d $depth  -i _ -m   $sub_func  $path/*.c $1/include/linux/*.h  -o func_tree_xx.txt 2> clow_err.txt
						  else :
							  cflow -T -d $depth  -i _ -m   $sub_func  $1/*/*.c $1/*/*/*.c  $1/include/linux/*.h  -o func_tree_xx.txt 2> clow_err.txt
						  fi
						  ./change_resume.py func_tree_xx.txt $tree_file
						fi
						func_level=-1
						func_index=0
						start_num=0
						Null_i=0
						continue
					fi
				fi
				echo "stat finish"
				break
			fi
			max_latency=0
			max_index=0
			max_level=0
			total_tmp=0
			start_num=0
			for(( j=$(($pointer_last_cflow+1)); j<${#cur_num[@]}; j++ )) do
				if [ ${cur_num[j]} -eq 0 ]; then
					continue
				fi

				if [ ${cur_level_stat[j]} -lt $cur_level ]; then
					continue
				fi
				total_tmp=$(($total_tmp + ${total_latency[j]}))
				if [ $max_latency -lt  ${total_latency[j]} ]; then
					max_latency=${total_latency[j]}
					max_index=${cur_index[j]}
					max_array_index=$j
					max_start=${total_start_num[j]}
					max_func=${cur_func[j]}
				fi
			done

			#some functions can't be hooked,  it's necessary to check these function's latency,
			total_double=$(($total_tmp * 20))
			if [[ $last_null_index -gt 0 || $total_double -lt $total_last_level ||  $total_double -eq 0 ]] && [ $Null_i -gt 1 ]; then
				if [ $last_null_index -eq 0 ]; then
					last_null_index=$Null_i;
				fi
				last_null_index=$(($last_null_index - 1))
				max_index=${null_index[$last_null_index]}
				max_latency=0
				start_num=${null_start[$last_null_index]}
				parent_func[0]=${null_func[$last_null_index]}
				null=1
				func_level=$(($cur_level-1))

			elif [ $max_latency -ne 0 ]; then
				total_last_level=$max_latency
				start_level=1
				start_num=$(($max_start + 1))
				if [ ! -z "$max_func" ]; then
					parent_func[1]=$max_func
				fi
			fi

			#recored parent function to find pointer function by hook parent, such as hook parent, and give the pointer
                        #to kernel, then %pF to print origin function to pointer
			res=$(func $func_level $(($max_index)) $start_num 1 $tree_file)

			res=$(func $func_level $(($max_index)) $start_num 2 $tree_file)

			if [ ! -z "$res" ]; then
				start_num=$(($res))
			fi
			func_index=$(($res))
			func_level=$(($func_level+1))
			last_level=$func_level
			level_first=1
			continue
			if [ $max_latency -ne 0 ]; then
				Null_i=0
				#total_last_level=$max_latency

				pointer=$(strstr ${cur_func[$max_array_index]}  "\->")
				if [ -z "$pointer" ]; then
					pointer=$(strstr ${cur_func[$max_array_index]}  "INDIRECT_CALL_INET")
				fi
				if [[ $max_array_index -lt 1000000000 &&  ! -z "$pointer" ]]; then
					#echo $max_array_index ":"  $pointer ":" ${cur_func[$max_array_index]}
					echo 2-----cflow next level:$1, $sub_func---------
					cflow -T -d 8 -i _  -m   ${cur_pointer_func[$max_array_index]}  /data/tkernel4/*.c /data/tkernel4/*/*.c /data/tkernel4/*/*/*.c  /data/tkernel4/include/linux/*.h  -o func_tree_xx.txt 2> clow_err.txt
					./change_resume.py func_tree_xx.txt func_tree.txt
					func_level=-1
					func_index=0
					start_num=0
					continue
				fi
			fi
			#printf "total lantecy: %10d, index: %4d, parent index:%4d, level:%2d, this:%10d\n" $max_latency $func_index $max_index $func_level $total_last_level
			continue
		fi

		#hook function, catch data, print to data.txt
		if [ $5 == 1 ]; then
		    pointer=$(strstr $res  "\->")
		    if [ -z "$pointer" ]; then
			    pointer=$(strstr $res  "INDIRECT_CALL_INET")
		    fi
		    cur_pointer_func[i]=$res
		    #only pointer need
		    #echo ------pointer:$pointer, $res
		    if [ ! -z "$pointer" ]; then
			    trace_func={}
			    trace_func[0]="-0 $res"
			    trace_func[1]="-1 ${parent_func[0]}"
			    trace_func[2]=" -2 ${parent_func[1]}"
			    echo --- pointer: ${trace_func[@]} ---
			    ./os_stat_blongm  ${trace_func[@]}  -n 1 -i $print_first -t $2 -b 0 -h $4 --ptr 1 -p $6 -x  &> data.txt
			    cur_pointer_func[i]=$(get_func)
		    fi
		    tmp_func[m]="-$m ${cur_pointer_func[i]}"
		    if [ -z "${cur_pointer_func[i]}" ]; then
		      tmp_func[m]="-$m $res"
		    fi
		    #long opt, need --
		    if [ $m -gt 9 ]; then
			tmp_func[m]="-${tmp_func[m]}"
		    fi
		    #echo 000000000000001 $res, ${tmp_func[m]}, $m, ${cur_pointer_func[i]}, ${parent_func[0]}, ${parent_func[1]}
		    cur_num[$i]=0
		    m=$(($m+1))
		else :
		    printf "%-40s %-40s %-40s\n" current:$res\(\), parent:${parent_func[0]}\(\), parent:${parent_func[1]}\(\)
		    ./os_stat_blongm  -0 $res -1 ${parent_func[0]} -2 ${parent_func[1]} -n 1 -i $print_first -t $2 -b 0 -h $4 --ptr 2 -p $6 -x &> data.txt
		    ./checkconfig_origngrap.py data.txt
		    if [ $print_first -eq 1 ]; then
			    print_first=0
			    cat data.txt
			    continue
		    fi
		    old_index=$func_index
		fi	
		
		#输出文件内容
		cur_level=$(($func_level+1))
		if [ $level_first -eq 0 ]; then
			func_index=$(($func_index+1))
		fi
		level_first=0
		if [ $5 == 0 ]; then
		    #get the data
		    tmp_num=$(num 0)
		    #echo 000000000000000 $tmp_num
		    if [ ! -n "$tmp_num" ] || [ $tmp_num -eq 0 ] ; then
			    null_func[$Null_i]=$res
			    null_index[$Null_i]=$old_index
			    null_start[$Null_i]=$start_num
			    Null_i=$(($Null_i+1))
			    continue
		    fi
		    #record data
		    cur_num[i]=$tmp_num
		    tmp_latency=$(latency 0)
		    cur_latency[i]=$tmp_latency
		    #echo 000000000000000 $tmp_latency
		    total_latency[i]=`expr $tmp_latency \* $tmp_num`
		    cur_pointer_func[i]=$(get_func)
		fi
		total_start_num[i]=$start_num
		cur_level_stat[i]=$cur_level
		cur_index[i]=$((func_index))
		cur_real_index[i]=$real_index
		cur_func[i]=$res
		i=$(($i+1))

		if [ $start_level == 1 ]; then
		      parent_func[0]=$res
		      if [ ! -z "${cur_pointer_func[i]}" ]; then
			    parent_func[0]=${cur_pointer_func[i]}
		      fi
		fi
		start_level=0
		max_array_index=1000000000

		if [ $5 == 0 ]; then
		    for(( j=0; j<${#cur_num[@]}; j++ )) do
			    if [ ${cur_num[j]} != 0 ]; then
				    tmp_index=${cur_index[j]}
				    if [ ! -z "$start_num" ]; then
					tmp_index=($tmp_index - $start_num)
				    fi
				    printf "******real index:%10d, index: %10d, level: %2d, num::%8d, latency:%10d ns, total latency:%10d ns, func:%s, origin:%s\n" ${cur_real_index[j]} $tmp_index ${cur_level_stat[j]} ${cur_num[j]} ${cur_latency[j]} ${total_latency[j]} ${cur_func[j]} ${cur_pointer_func[j]}
			    fi
		    done
		fi

		continue
	done
}

#main
function main()
{
	#check paremter counts
	if [ $# -lt 2 ]; then
		echo "[Error]: t-ops os_stat linux_dir/"which afford source code" function/"which scan from this function" flags/"0:hot path, 1: one function"  hot/"if first scan hot path, hot=1, other case hot=0" \
		  such as: t-ops os_stat /data/tkernel4 vfs_read 0 0"
		return
	fi

	#check user tools
	if [ ! -f "./os_stat_blongm" ]; then
		echo "user stat file: os_stat_blongm is not exist"
	fi

	#scan function list
	check_cflow

	#测试前,先卸载驱动,然后安装,防止程序强制退出时,ftrace部分没有卸载hook
	rmmod os_aware
	if [ $# -eq 0 ]; then
		echo "need install os stat module: ./stat.sh os_aware_xxx.ko"
		return
	fi
	insmod $1
	echo "install os stat kernel module:" $1 "successfully"

	#scan function list
	cflow_func $2 $3 $4 $6

	#start to work
	do_work $2 $5 $6 $7 $8 $9
}

main $*


